package joc.performance;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.junit.Test;

import com.googlecode.charts4j.AxisLabels;
import com.googlecode.charts4j.AxisLabelsFactory;
import com.googlecode.charts4j.AxisStyle;
import com.googlecode.charts4j.AxisTextAlignment;
import com.googlecode.charts4j.Color;
import com.googlecode.charts4j.DataUtil;
import com.googlecode.charts4j.Fills;
import com.googlecode.charts4j.GCharts;
import com.googlecode.charts4j.Line;
import com.googlecode.charts4j.LineChart;
import com.googlecode.charts4j.LineStyle;
import com.googlecode.charts4j.LinearGradientFill;
import com.googlecode.charts4j.Plots;
import com.googlecode.charts4j.Shape;

public class PerformanceTestExecutor {
	private static final String ENVIRONMENT = "(" + System.getProperty("os.name") + " "
			+ System.getProperty("os.version") + " " + System.getProperty("user.name") + " "
			+ System.getProperty("user.dir") + ")";
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yy HH:mm:ss");
	private static List<PerformanceTest> tests = new ArrayList<PerformanceTest>(Arrays.asList(
			new FieldInvariantPerformanceTest(), new ParameterPreConditionPerformanceTest(),
			new PostConditionPerformanceTest(), new CustomPostConditionPerformanceTest(),
			new CustomPreConditionPerformanceTest(), new CustomInvariantPerformanceTest()));
	private Logger logger = Logger.getLogger(PerformanceTestExecutor.class);

	@Test
	public void logPerformance() throws Exception {
		String dateString = DATE_FORMAT.format(new Date());
		for (PerformanceTest test : tests) {
			test.test();
			logger.info(getLogMessage(dateString, test, "", test.getEnabledResult().duration,
					test.getDisabledResult().duration));
			logger.info(getLogMessage(dateString, test, "Transformation",
					test.getEnabledResult().transformationDuration, test.getDisabledResult().transformationDuration));
		}
		createChart();
	}

	private String getLogMessage(String dateString, PerformanceTest test, String appendix, long enabledDuration,
			long disabledDuration) {
		return dateString + " " + test.getDescription() + appendix + ": " + enabledDuration + "ns vs "
				+ disabledDuration + "ns - " + (enabledDuration - disabledDuration) + "ns overhead " + ENVIRONMENT;
	}

	private void createChart() throws Exception {
		Map<String, Map<Date, Long>> data = new HashMap<String, Map<Date, Long>>();
		List<Date> dates = new ArrayList<Date>();
		getLogData(data, dates);
		List<String> sortedTests = new ArrayList<String>(data.keySet());
		Collections.sort(sortedTests);
		Collections.sort(dates);
		BufferedWriter writer = new BufferedWriter(new FileWriter("joc-performance.html"));
		for (String test : sortedTests) {
			LineChart chart = createChartForTest(data, dates, test);
			writer.append("<img src=\"" + chart.toURLString() + "\"/><br/>");
		}
		writer.close();
	}

	private LineChart createChartForTest(Map<String, Map<Date, Long>> data, List<Date> dates, String test) {
		List<Long> testData = getDataForTest(data, dates, test);
		long max = 0;
		for (Long entry : testData) {
			max = Math.max(max, entry.longValue());
		}
		Line line = Plots.newLine(DataUtil.scaleWithinRange(0, max, testData), Color.SKYBLUE);
		line.setLineStyle(LineStyle.newLineStyle(3, 1, 0));
		line.addShapeMarkers(Shape.CIRCLE, Color.BLACK, 8);

		LineChart chart = GCharts.newLineChart(line);
		String unit = "ns";
		if (max > 10000) {
			max /= 1000;
			unit = "μs";
			if (max > 10000) {
				max /= 1000;
				unit = "ms";
			}
		}
		chart.setSize(600, 450);
		chart.setTitle(test + " (" + unit + ")", Color.WHITE, 14);
		chart.setGrid(25, 25, 3, 2);

		chart.setBackgroundFill(Fills.newSolidFill(Color.newColor("1F1D1D")));
		LinearGradientFill fill = Fills.newLinearGradientFill(0, Color.newColor("363433"), 100);
		fill.addColorAndOffset(Color.newColor("2E2B2A"), 0);
		chart.setAreaFill(fill);
		AxisStyle axisStyle = AxisStyle.newAxisStyle(Color.WHITE, 12, AxisTextAlignment.CENTER);
		axisStyle.setDrawTickMarks(true);
		AxisLabels yAxis = AxisLabelsFactory.newNumericRangeAxisLabels(0, max);
		yAxis.setAxisStyle(axisStyle);
		chart.addYAxisLabels(yAxis);
		return chart;
	}

	private List<Long> getDataForTest(Map<String, Map<Date, Long>> data, List<Date> dates, String test) {
		List<Long> testData = new ArrayList<Long>();
		for (Date date : dates) {
			if (data.get(test).containsKey(date)) {
				Long value = data.get(test).get(date);
				testData.add(value);
			} else {
				testData.add(Long.valueOf(0));
			}
		}
		return testData;
	}

	private void getLogData(Map<String, Map<Date, Long>> data, List<Date> dates) throws FileNotFoundException,
			IOException, ParseException {
		BufferedReader reader = new BufferedReader(new FileReader(new File("joc-performance.log")));
		String logLine = reader.readLine();
		while (logLine != null) {
			Pattern p = Pattern.compile("^(.+? .+?) (.+?): (\\d+?)ns vs (\\d+?)ns - (\\d+?)ns overhead \\((.+?)\\)");
			Matcher m = p.matcher(logLine);

			if (m.find()) {
				if (!data.containsKey(m.group(2))) {
					data.put(m.group(2), new HashMap<Date, Long>());
				}
				Date date = DATE_FORMAT.parse(m.group(1));
				if (!dates.contains(date)) {
					dates.add(date);
				}
				data.get(m.group(2)).put(date, Long.valueOf(m.group(5)));
			}

			logLine = reader.readLine();
		}
	}
}
